/** \file    BuildProfiles.cpp
  * \brief   Fills profiles with correlation values.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/segm/src/BuildProfiles.cpp,v 1.1 2004/05/20 09:11:46 cscom1r4 Exp $
  */

#include "segm/Segmentation.h"
#include "BuildProfilesImpl.h"
#include "imglib/convert.h"
#include <algorithm>
#include "std_min_max.h"

static inline void eraseUsedPixels(Segment &g)
	{ g.usedPixels.Clear(); }

static void buildAutocorrelationProfiles(Segmentation &s, 
	const float_image & tgt, const ProfileParams & param)
{
	const float_image & src = *s.sourceImage();
	unsigned size =		param.maxDisplacement;
	unsigned selfSize = param.maxSelfDisplacement;
	int i;
	std::vector<YATL::Arr2d>	selfProfiles(s.count());
	std::vector<int_point>		pdispl(s.count());

	//find autocorrelation profiles
	for( i = 0; i != s.count(); ++i ) {
		Segment &g = s.segmentByIndex(i);
		pdispl[i] = g.pdisplacement(); 
		g.setPdisplacement( int_point(0,0) ); ///< no predicted displacement in autocorrelation
	}

	{
		ProfileParams p = param;
		p.maxDisplacement = selfSize;
		buildProfileInternal(s, src, src, p);
	}

	for( i = 0; i != s.count(); ++i ) {
		Segment &g = s.segmentByIndex(i);
		g.setPdisplacement( pdispl[i] ); ///< restore predicted displacement
		selfProfiles[i].Swap( g.profile );
	}

	{
		ProfileParams p = param;
		p.maxDisplacement += selfSize;
		buildProfileInternal(s, src, tgt, p);
	}
	std::for_each(s.segmentsBegin(), s.segmentsEnd(), eraseUsedPixels);

	int profileSize = param.maxDisplacement;
	int profileDblSize = 2*profileSize + 1;
	YATL::Arr2d tmp(profileDblSize, profileDblSize);

	///SSD of profile and self-profile
	for( i = 0; i != s.count(); ++i ) {
		YATL::Arr2d &profile = s.segmentByIndex(i).profile;
		int x,y,ksi,eta;
		for( y = 0; y != profileDblSize; ++y ) {
			for( x = 0; x != profileDblSize; ++x ) {
				double sum=0;
				for( eta = 0; eta != 2*selfSize+1; ++eta ) {
					for( ksi = 0; ksi != 2*selfSize+1; ++ksi ) {
						sum += fabs( profile(x+ksi,y+eta) - selfProfiles[i](ksi,eta) );
					}
				}
				tmp(x,y) = -sum;
			}
		}
		s.segmentByIndex(i).profileResize(profileSize);
		profile = tmp;
	}
}

static void buildForeignProfiles(Segmentation &s, 
	const float_image & tgt, const ProfileParams & param)
{
	const float_image & src = *s.sourceImage();
	bool abs = param.profileType == PROFILE_FOREIGN_ABS;

	int i;
	//predicted displecements should be zero for computations on the source frame, save them here
	std::vector<int_point> pdispl( s.count() ); 

	//we already have mean(&dispersion) found in segments
	//copy mean&dispersion and pdisplacement from inbound regions to foreign segments
	for( i = 0; i != s.count(); ++i ) {
		Segment &f = s.segmentByIndex(i);
		if( f.foreign ) {
			Segment *b = s.pSegmentByColor(f.color);
			f.meanColor = b->meanColor;
			f.colorDispersion = b->colorDispersion;
			pdispl[i] = b->pdisplacement();
		} else {
			pdispl[i] = f.pdisplacement();
		}
	}
	for( i = 0; i != s.count(); ++i )
		s.segmentByIndex(i).setPdisplacement( int_point(0,0) );

	//original summed (limited) difference between points under foreign segments and mean border colors
	DoubleArr orgK( s.count() );
	{
		ProfileParams p = param;
		p.distinction = DIST_DISPERSION;
		p.maxDisplacement = 0;
		buildProfileInternal(s, src, src, p); //profile 1x1, rather quick
	}
	for( i = 0; i != s.count(); ++i ) { 
		Segment &f = s.segmentByIndex(i);
		orgK[i] = f.profile(0,0); 
		//restore predicted displacements
		f.setPdisplacement(pdispl[i]);
	}

	if (param.distinction == DIST_DISPERSION)
		buildProfileInternal(s, src, tgt, param);
	else { //correlation
		//replace points under foreign segments on mean color of border segments
		float_image src2(src.width(), src.height());
		float_image::iterator p;
		for (p = src2.begin(); p != src2.end(); ++p) {
			int si = s.segmentIndex(p);
			Segment *f;
			if ( si==-1 || !(f=&s.segmentByIndex(si))->foreign ) {
				src2[p] = src[p];
			} else {
				int bi = s.segmentIndex(f->color);
				assert( bi != -1 );
				src2[p] = s.segmentByIndex(si).meanColor;
			}
		}
		buildProfileInternal(s, src2, tgt, param);
	}

	//add minus foreign profile in border profile
	for( i = 0; i != s.count(); ++i ) {
		Segment &f = s.segmentByIndex(i);
		if( !f.foreign ) continue;
		Segment *b = s.pSegmentByColor(f.color);
		assert( b && b->active );
		double c, c1;
		if (param.foreignRho < 0) {
			int s = b->pixelCount;
			int s1 = f.pixelCount;
			c = 2*double(s) / double(s+s1);
			c1 = 2*double(s1) / double(s+s1);
		} else {
			c = 1.0;
			c1 = param.foreignRho;
		}
		YATL::Arr2d::iterator fi = f.profile.begin();
		YATL::Arr2d::iterator bi = b->profile.begin();
		if( abs ) {	
			for( ; fi != f.profile.end(); ++fi, ++bi ) *bi = c * *bi - c1*fabs( *fi - orgK[i] );
		} else {
			for( ; fi != f.profile.end(); ++fi, ++bi ) *bi = c * *bi + c1*std::min( orgK[i] - *fi, 0.0 );
		}
	}
}

////////////////
// dispatcher //
////////////////

void Segmentation::buildProfiles(const float_image & tgt, const ProfileParams & param,
							 	 bool activeOnly)
{
	assert(m_sourceImage);
	const float_image &src = *sourceImage();

	colorStatistics(true, param.factorSqSigma);

	switch (param.profileType) {
	case PROFILE_INTERNAL:
		buildProfileInternal(*this, src, tgt, param, activeOnly);
		break;
	case PROFILE_AUTOCORRELATION:
		assert(!activeOnly);
		buildAutocorrelationProfiles(*this, tgt, param);
		break;
	case PROFILE_FOREIGN_ABS:
	case PROFILE_FOREIGN_MIN:
		assert(!activeOnly);
		buildForeignProfiles(*this, tgt, param);
		break;
	default:
		assert(false);
	}
}

